﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using NUnit.Framework;
using RenewalProjects.Libraries.Common.Testing.Helpers;
using RenewalProjects.Libraries.Wcf.EasyProxy.Pools;
using RenewalProjects.Libraries.Wcf.EasyProxy.Tests.TestBuilders;
using RenewalProjects.Libraries.Wcf.EasyProxy.Tests.TestData;

namespace RenewalProjects.Libraries.Wcf.EasyProxy.Tests.FixtureHelpers
{
	public class ResourceListPoolFixtureHelper
	{
		private readonly ManagedTestItemBuilder _testItemBuilder;
		private readonly ResourceListPool<TestItem> _pool;
		private int _assertCalled;

		public ResourceListPoolFixtureHelper(int poolCapacity)
		{
			_testItemBuilder = new ManagedTestItemBuilder();
			_pool = new ResourceListPool<TestItem>(poolCapacity);
			_pool.ResourceItemFactory = _testItemBuilder.DelegateFactory;
		}

		public ResourceListPoolFixtureHelper AddItems(int amount)
		{
			_testItemBuilder.AddAmount(amount);
			return this;
		}

		public ResourceListPoolFixtureHelper AddItems(IEnumerable<TestItem> items)
		{
			foreach (TestItem item in items)
			{
				_testItemBuilder.Add(item);
			}

			return this;
		}

		public ResourceListPoolFixtureHelper ExpireItem(int index)
		{
			_testItemBuilder.PrecreatedItems[index].Expired = true;
			return this;
		}

		public ResourceListPoolFixtureHelper ExpireItems(IEnumerable<int> indicies)
		{
			foreach (int index in indicies)
			{
				_testItemBuilder.PrecreatedItems[index].Expired = true;
			}
			return this;
		}

		public ResourceListPoolFixtureHelper OccupyItem(int index, bool occupied)
		{
			_testItemBuilder.PrecreatedItems[index].InUse = occupied;
			return this;
		}

		public ResourceListPoolFixtureHelper OccupyItems(IEnumerable<int> indicies, bool occupied)
		{
			foreach (int index in indicies)
			{
				_testItemBuilder.PrecreatedItems[index].InUse = occupied;
			}
			return this;
		}

		public ResourceListPoolFixtureHelper AssertItems(IEnumerable<int> indicies, Func<TestItem, bool> assertFunc)
		{
			_assertCalled++;
			
			foreach (int index in indicies)
			{
				Assert.IsTrue(assertFunc(_testItemBuilder.PrecreatedItems[index]), "Assertion not true - {0}", _assertCalled);
			}
			return this;
		}

		public ResourceListPoolFixtureHelper CheckFactoryCallCount(int expectedAmount)
		{
			Assert.AreEqual(expectedAmount, _testItemBuilder.FactoryCallCount);
			return this;
		}

		public ResourceListPoolFixtureHelper RefreshResources()
		{
			_pool.RefreshResources();
			return this;
		}

		public RetrievedTestItemHelper ClientsRequestAvailableItems(int threads)
		{
			if(threads < 1)
			{
				throw new ArgumentOutOfRangeException("threads");
			}

			if(threads == 1)
			{
				TestItem item = _pool.GetAvailableResource();
				return new RetrievedTestItemHelper(this, item);
			}
			else
			{
				ConcurrentBag<TestItem> bag = new ConcurrentBag<TestItem>();
				new ThreadGroup(
					()=>
					{
					        TestItem item = _pool.GetAvailableResource();
					        bag.Add(item);
					}, threads)
				    .Start()
				    .WaitForThreads();

				return new RetrievedTestItemHelper(this, bag);
			}
		}

		public class RetrievedTestItemHelper
		{
			private readonly ResourceListPoolFixtureHelper _resourceListPoolHelper;
			private readonly IEnumerable<TestItem> _receivedItems;
			private IEnumerator<TestItem> _enumerator;

			public RetrievedTestItemHelper(ResourceListPoolFixtureHelper resourceListPoolHelper, TestItem item)
				: this(resourceListPoolHelper, new List<TestItem>{item})
			{
			}

			public RetrievedTestItemHelper(ResourceListPoolFixtureHelper resourceListPoolHelper, IEnumerable<TestItem> items)
			{
				_resourceListPoolHelper = resourceListPoolHelper;
				_receivedItems = items;
				_enumerator = _receivedItems.GetEnumerator();
				ResetIterator();
			}

			public RetrievedTestItemHelper ResetIterator()
			{
				_enumerator.Reset();
				_enumerator.MoveNext();

				return this;
			}

			public RetrievedTestItemHelper NextReturnedItem()
			{
				_enumerator.MoveNext();
				return this;
			}

			public RetrievedTestItemHelper VerifyCurrentItemWithPrecreatedItem(int precreatedItemIndex)
			{
				_resourceListPoolHelper.AssertItems(new[] {precreatedItemIndex},
					(i)=>i.Equals(_enumerator.Current));
				return this;
			}

			public RetrievedTestItemHelper CurrentItemIsNull()
			{
				Assert.IsNull(_enumerator.Current);
				return this;
			}

			public RetrievedTestItemHelper CheckAllReturnedItemsFor(int exactExpectedItemCount,
			                                                        Func<TestItem, bool> predicate)
			{
				ResetIterator();

				int count = 0;
				do
				{
					if(predicate(_enumerator.Current))
					{
						count++;
					}
				} while(_enumerator.MoveNext());

				Assert.AreEqual(exactExpectedItemCount, count);

				return this;
			}

			public ResourceListPoolFixtureHelper Done()
			{
				return _resourceListPoolHelper;
			}
		}
	}
}